/**
 * Copyright (c) 2011, www.quartzsource.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.quartzsource.core

import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.{ Map => MMap }

object Parsers {

  /**
   * This code assumes that a manifest is stitched together with newline
   * ('\n') characters.
   */
  def parseManifest(l: Binary): ManifestMap = {
    def splitLines(l: Binary, list: List[Binary]): (Binary, List[Binary]) = l.isEmpty match {
      case true => (l, list)
      case false => {
        val (start, withSplit) = l.span(_ != '\n')
        splitLines(withSplit.tail, start :: list)
      }
    }
    val (_, lines) = splitLines(l, Nil)
    val mfdict: MMap[String, NodeId] = MMap.empty
    val fdict: MMap[String, String] = MMap.empty
    lines.foreach(l => {
      val (f, withSplit) = l.span(_ != '\0')
      val n = withSplit.tail
      //since the filename may not be encoded in UTF-8 the filename may be wrong
      //there should be a repository configuration setting which identifies the encoding
      val filename = bytesToString(f)
      if (n.size > 40) {
        val flags = bytesToString(n.drop(40))
        //DEBUG check
        if (flags.matches("[^xl]")) throw new QuartzException("Unexpected flags: " + flags)
        fdict(filename) = flags
        mfdict(filename) = NodeId.unhexlify(bytesToString(n.take(40)))
      } else {
        mfdict(filename) = NodeId.unhexlify(bytesToString(n))
      }
    })
    new ManifestMap(mfdict, fdict)
  }

  //parseIndex() is implemented in RevlogIO

  def parseDirstate(dmap: MMap[String, DirStateTuple], copymap: MMap[String, String],
    st: Binary): List[NodeId] = {
    val parents = List(new NodeId(st.take(20)), new NodeId(st.slice(20, 40)))
    val eSize = 17
    var pos1 = 40
    val l = st.size
    // the inner loop
    while (pos1 < l) {
      val pos2 = pos1 + eSize
      val data = st.slice(pos1, pos2)
      val e = (DirState.parseState(data(0)), readInt(data, 1), readInt(data, 5), readInt(data, 9))
      pos1 = pos2 + readInt(data, 13)
      var fdata = st.slice(pos2, pos1)
      val f = if (fdata.exists(_ == 0)) {
        val (ff, c) = fdata.span(_ != 0)
        copymap(bytesToString(ff)) = bytesToString(c.tail) // drop the splitter (zero byte)
        ff
      } else {
        fdata
      }
      dmap(bytesToString(f)) = e
    }
    parents
  }

  def readInt(data: IndexedSeq[Byte], start: Int): Int = {
    val ch1 = data(start) & 255
    val ch2 = data(start + 1) & 255
    val ch3 = data(start + 2) & 255
    val ch4 = data(start + 3) & 255
    ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0))
  }

  def readLong(data: Binary): Long = {
    (data(0).toLong << 56) + // unsigned
      ((data(1) & 255).toLong << 48) +
      ((data(2) & 255).toLong << 40) +
      ((data(3) & 255).toLong << 32) +
      ((data(4) & 255).toLong << 24) +
      ((data(5) & 255) << 16) +
      ((data(6) & 255) << 8) +
      ((data(7) & 255) << 0)
  }
}
